/*
 *
 * Copyright (c) 2020 The Raptor Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#ifndef RAPTOR_LITE_UTILS_SYNC_H_
#define RAPTOR_LITE_UTILS_SYNC_H_

#include <stdint.h>

#ifdef _WIN32
#include <windows.h>

typedef CRITICAL_SECTION raptor_mutex_t;
typedef CONDITION_VARIABLE raptor_condvar_t;
typedef INIT_ONCE raptor_once_t;
typedef SRWLOCK raptor_rwlock_t;

#define RAPTOR_ONCE_INIT INIT_ONCE_STATIC_INIT

// mutex
#define RaptorMutexInit(m)    InitializeCriticalSection(m)
#define RaptorMutexLock(m)    EnterCriticalSection(m)
#define RaptorMutexUnlock(m)  LeaveCriticalSection(m)
#define RaptorMutexDestroy(m) DeleteCriticalSection(m)

// condvar
#define RaptorCondVarInit(cv)      InitializeConditionVariable(cv)
#define RaptorCondVarDestroy(cv)   (cv)
#define RaptorCondVarSignal(cv)    WakeConditionVariable(cv)
#define RaptorCondVarBroadcast(cv) WakeAllConditionVariable(cv)

// Return 1 in timeout, 0 in other cases
int RaptorCondVarWait(raptor_condvar_t *cv, raptor_mutex_t *mutex, int64_t timeout_ms);

// rwlock
#define RaptorRWLockInit(lock)    InitializeSRWLock(lock)
#define RaptorRWLockDestroy(lock) (void)(lock)

#define RaptorRWLockReadLock(lock)    AcquireSRWLockShared(lock)
#define RaptorRWLockTryReadLock(lock) TryAcquireSRWLockShared(lock)
#define RaptorRWLockReadUnlock(lock)  ReleaseSRWLockShared(lock)

#define RaptorRWLockWriteLock(lock)    AcquireSRWLockExclusive(lock)
#define RaptorRWLockTryWriteLock(lock) TryAcquireSRWLockExclusive(lock)
#define RaptorRWLockWriteUnlock(lock)  ReleaseSRWLockExclusive(lock)

#else
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>

typedef pthread_mutex_t raptor_mutex_t;
typedef pthread_cond_t raptor_condvar_t;
typedef pthread_once_t raptor_once_t;
typedef pthread_rwlock_t raptor_rwlock_t;

#define RAPTOR_ONCE_INIT PTHREAD_ONCE_INIT

#define RaptorMutexInit(m)         pthread_mutex_init(m, NULL)
#define RaptorMutexLock(m)         pthread_mutex_lock(m)
#define RaptorMutexUnlock(m)       pthread_mutex_unlock(m)
#define RaptorMutexDestroy(m)      pthread_mutex_destroy(m)

// condvar
void RaptorCondVarInit(raptor_condvar_t *cv);
#define RaptorCondVarDestroy(cv)   pthread_cond_destroy(cv)
#define RaptorCondVarSignal(cv)    pthread_cond_signal(cv)
#define RaptorCondVarBroadcast(cv) pthread_cond_broadcast(cv)

// Return 1 in timeout, 0 in other cases
int RaptorCondVarWait(raptor_condvar_t *cv, raptor_mutex_t *mutex, int64_t timeout_ms);

// rwlock
#define RaptorRWLockInit(lock)     pthread_rwlock_init(lock, NULL)
#define RaptorRWLockDestroy(lock)  pthread_rwlock_destroy(lock)

#define RaptorRWLockReadLock(lock)    pthread_rwlock_rdlock(lock)
#define RaptorRWLockTryReadLock(lock) pthread_rwlock_tryrdlock(lock)
#define RaptorRWLockReadUnlock(lock)  pthread_rwlock_unlock(lock)

#define RaptorRWLockWriteLock(lock)    pthread_rwlock_wrlock(lock)
#define RaptorRWLockTryWriteLock(lock) pthread_rwlock_trywrlock(lock)
#define RaptorRWLockWriteUnlock(lock)  pthread_rwlock_unlock(lock)

#endif

void RaptorOnceInit(raptor_once_t *once, void (*init_function)(void));

namespace raptor {

class Mutex final {
public:
    Mutex() { RaptorMutexInit(&mtx_); }
    ~Mutex() { RaptorMutexDestroy(&mtx_); }

    Mutex(const Mutex &) = delete;
    Mutex &operator=(const Mutex &) = delete;

    void Lock() { RaptorMutexLock(&mtx_); }
    void Unlock() { RaptorMutexUnlock(&mtx_); }

    operator raptor_mutex_t *() { return &mtx_; }

private:
    raptor_mutex_t mtx_;
};

class AutoMutex final {
public:
    explicit AutoMutex(Mutex *m)
        : mtx_(*m) {
        RaptorMutexLock(mtx_);
    }
    explicit AutoMutex(raptor_mutex_t *m)
        : mtx_(m) {
        RaptorMutexLock(mtx_);
    }
    ~AutoMutex() { RaptorMutexUnlock(mtx_); }

    AutoMutex(const AutoMutex &) = delete;
    AutoMutex &operator=(const AutoMutex &) = delete;

private:
    raptor_mutex_t *mtx_;
};

class ConditionVariable final {
public:
    ConditionVariable() { RaptorCondVarInit(&cv_); }
    ~ConditionVariable() { RaptorCondVarDestroy(&cv_); }

    ConditionVariable(const ConditionVariable &) = delete;
    ConditionVariable &operator=(const ConditionVariable &) = delete;

    void Signal() { RaptorCondVarSignal(&cv_); }
    void Broadcast() { RaptorCondVarBroadcast(&cv_); }

    int Wait(Mutex *m) { return RaptorCondVarWait(&cv_, *m, -1); }

    // return 1 means timeout
    int Wait(Mutex *m, int64_t timeout_ms) { return RaptorCondVarWait(&cv_, *m, timeout_ms); }

    operator raptor_condvar_t *() { return &cv_; }

private:
    raptor_condvar_t cv_;
};

class LockInterface {
public:
    virtual ~LockInterface() {}

    virtual void Lock()    = 0;
    virtual void Unlock()  = 0;
    virtual bool TryLock() = 0;
};

class RWLock final {
public:
    RWLock() { RaptorRWLockInit(&lock_); }
    ~RWLock() { RaptorRWLockDestroy(&lock_); }

    void ReadLock() { RaptorRWLockReadLock(&lock_); }
    void ReadUnlock() { RaptorRWLockReadUnlock(&lock_); }

    void WriteLock() { RaptorRWLockWriteLock(&lock_); }
    void WriteUnlock() { RaptorRWLockWriteUnlock(&lock_); }

    bool TryReadLock();
    bool TryWriteLock();

    operator raptor_rwlock_t *() { return &lock_; }

private:
    raptor_rwlock_t lock_;
};

class ReadLock final : public LockInterface {
public:
    explicit ReadLock(RWLock *rwl)
        : lock_(*rwl) {}
    explicit ReadLock(raptor_rwlock_t *rwl)
        : lock_(rwl) {}

    ReadLock(const ReadLock &) = delete;
    ReadLock &operator=(const ReadLock &) = delete;

    void Lock() override { RaptorRWLockReadLock(lock_); }
    void Unlock() override { RaptorRWLockReadUnlock(lock_); }

    bool TryLock() override;

private:
    raptor_rwlock_t *lock_;
};

class WriteLock final : public LockInterface {
public:
    explicit WriteLock(RWLock *rwl)
        : lock_(*rwl) {}
    explicit WriteLock(raptor_rwlock_t *rwl)
        : lock_(rwl) {}

    WriteLock(const WriteLock &) = delete;
    WriteLock &operator=(const WriteLock &) = delete;

    void Lock() override { RaptorRWLockWriteLock(lock_); }
    void Unlock() override { RaptorRWLockWriteUnlock(lock_); }

    bool TryLock() override;

private:
    raptor_rwlock_t *lock_;
};

class AutoRWLock final {
public:
    explicit AutoRWLock(LockInterface *lock)
        : lock_(lock) {
        lock->Lock();
    }

    ~AutoRWLock() { lock_->Unlock(); }

    AutoRWLock(const AutoRWLock &) = delete;
    AutoRWLock &operator=(const AutoRWLock &) = delete;

private:
    LockInterface *lock_;
};
} // namespace raptor

#endif // RAPTOR_LITE_UTILS_SYNC_H_
